tool
extends ArrayMesh

class_name ExtrudedCubeMesh

export(Vector3) var offset: Vector3 = Vector3(0, 0, 0) setget set_offset
export(Vector3) var size: Vector3 = Vector3(2, 2, 2) setget set_size
export(PoolVector3Array) var extrude: PoolVector3Array = PoolVector3Array() setget set_extrude

class MeshBuilder:
	var offset: Vector3
	var size: Vector3
	var extrude: PoolRealArray

	var st: SurfaceTool = SurfaceTool.new()
	var arrays: Array

	var p: PoolVector3Array = PoolVector3Array()

	func _add_face(v: PoolVector3Array, e: Vector3):
		if e.length() > 1e-4:
			for i in range(v.size()):
				var j: int = i + 1
				if j >= v.size():
					j = 0
				st.add_triangle_fan(PoolVector3Array([v[i], v[j], v[j] + e, v[i] + e]))

			for i in range(v.size()):
				v[i] += e

		st.add_triangle_fan(v)

	func _add_cube_face(NX: int, NY: int, NZ: int):
		var index = NY + NZ * 2

		var v: PoolVector3Array = PoolVector3Array()
		v.push_back(Vector3(p[0].x, p[0].y, p[0].z))
		v.push_back(Vector3(p[NZ].x, p[NX].y, p[NY].z))
		v.push_back(Vector3(p[1-NX].x, p[1-NY].y, p[1-NZ].z))
		v.push_back(Vector3(p[NY].x, p[NZ].y, p[NX].z))
		_add_face(v, Vector3(-NX, -NY, -NZ) * extrude[index + 3])

		v = PoolVector3Array()
		v.push_back(Vector3(p[1].x, p[1].y, p[1].z))
		v.push_back(Vector3(p[1-NY].x, p[1-NZ].y, p[1-NX].z))
		v.push_back(Vector3(p[NX].x, p[NY].y, p[NZ].z))
		v.push_back(Vector3(p[1-NZ].x, p[1-NX].y, p[1-NY].z))
		_add_face(v, Vector3(NX, NY, NZ) * extrude[index])

	func _init(p_offset: Vector3, p_size: Vector3, p_extrude: PoolVector3Array):
		offset = p_offset
		size = p_size

		if p_extrude.size() >= 2:
			extrude = PoolRealArray([ \
			p_extrude[0].x, p_extrude[0].y, p_extrude[0].z, \
			p_extrude[1].x, p_extrude[1].y, p_extrude[1].z])
		elif p_extrude.size() == 1:
			extrude = PoolRealArray([ \
			p_extrude[0].x, p_extrude[0].y, p_extrude[0].z, \
			p_extrude[0].x, p_extrude[0].y, p_extrude[0].z])
		else:
			extrude = PoolRealArray([0, 0, 0, 0, 0, 0])

		p.push_back(offset + size * -0.5)
		p.push_back(offset + size * 0.5)

		st.begin(Mesh.PRIMITIVE_TRIANGLES)

		_add_cube_face(1, 0, 0)
		_add_cube_face(0, 1, 0)
		_add_cube_face(0, 0, 1)

		st.generate_normals()
		st.index()
		arrays = st.commit_to_arrays()

func _create_mesh():
	var mat: Material = null
	if get_surface_count() > 0:
		# backup the original material
		mat = surface_get_material(0)

		# remove all faces
		while get_surface_count() > 0:
			surface_remove(0)

	var b: MeshBuilder = MeshBuilder.new(offset, size, extrude)

	add_surface_from_arrays(PRIMITIVE_TRIANGLES, b.arrays)
	surface_set_material(0, mat)

	emit_signal("changed")

func _init():
	_create_mesh()

func set_offset(p_offset: Vector3):
	if offset != p_offset:
		offset = p_offset
		_create_mesh()

func set_size(p_size: Vector3):
	if size != p_size:
		size = p_size
		_create_mesh()

func set_extrude(p_extrude: PoolVector3Array):
	if extrude != p_extrude:
		extrude = p_extrude
		_create_mesh()
